oo::class create Language {
    constructor {renamedPadWidget} {
        # CONFIG TAG
        $renamedPadWidget tag configure K1 -foreground #088A85
        $renamedPadWidget tag configure K2 -foreground #0B6138
        $renamedPadWidget tag configure K3 -foreground #9A2EFE
        $renamedPadWidget tag configure Variable -foreground #FF8000
        $renamedPadWidget tag configure Proc -foreground #58ACFA
        $renamedPadWidget tag configure DoubleQuote -foreground #DF0101
        set slant [my CreateSlantFont $renamedPadWidget]
        $renamedPadWidget tag configure Comment -foreground #8A4B08 -font $slant
    }

    method CreateSlantFont {renamedPadWidget} {
        set font [lindex [$renamedPadWidget configure -font] end]
        set family [lindex $font 0]
        set size [lindex $font 1]
        set rest [lreplace $font 0 1]
        set i 0
        foreach e $rest {
            if {$e == "italic" || $e == "roman"} {
                return [list $family $size {*}[lreplace $rest $i $i italic]]
            }
            incr i
        }
        return [list $family $size {*}$rest italic]
    }

    method getQuoteTag {{quoteTyep \"}} {}
    method getVariableTag {} {}
    method getCommentTag {} {}
    # method getKeyWordTag {word} {}
    method getDefinitionTag {} {}
    method getBraceTag {} {}
    method getCurrentLinetag {} {}

    method getEscapeChar {} {}
    method getDefinitionWords {} {}
    method getKeyWordsGroup1 {} {}
    method getKeyWordsGroup2 {} {}
    method getBlockEnd {textWidget blockHeadIndex} {}
    method getIndentChar {} {}
    method getUnindentChar {} {}
    method getTabSpacing {} {}

    method getBlockHead {textWidget index} {}
    method getBlockEnd {textWidget headIndex} {}
    method updateIndent {textWidget index} {}

    method getKeyWordTag {word} {
        # Not use yet!
        # Use Keyword getTag instead
        set class [info object class [self object]]
        set methods [info class methods $class]
        foreach m $methods {
            if [regexp {getKeyWordsGroup(\d)} $m _match id] {
                foreach k [my $m] {
                    if {$k == $word} {
                        return K$id
                    }
                }
            }
        }
    }

    method getLineHeadSpacing {textWidget index} {
        set line [$textWidget get "$index linestart" "$index lineend"]
        return [expr [string length $line]-[string length [string trimleft $line]]]
    }

    method isEscapedPosition? {str id} {
        if {$id == 0} {return 0}
        set head [string range $str 0 $id-1]
        set trimedHead [string trimright $head \\]
        return [expr ([string length $head]-[string length $trimedHead])%2]
    }

    method getPairIndices {content pair} {
        set ret {}
        foreach _ids [regexp -all -inline -indices "\[\\[lindex $pair 0]\\[lindex $pair 1]\]" $content] {
            foreach {id _} $_ids {}
            if ![my isEscapedPosition? $content $id] {
                lappend ret $id
            }
        }
        return $ret
    }

    method pushPairStack {stack p pair} {
        foreach {h _} $pair {}
        set stackEnd [lindex $stack end]
        if {$stackEnd == ""} {
            lappend stack $p
        } else {
            if {$stackEnd == $h} {
                if {$p == $h} {
                    lappend stack $p
                } else {
                    set stack [lreplace $stack end end]
                }
            } else {
                return -code error disorder
            }
        }

        return $stack
    }

    method getPairBoundary {textWidget index pair dir} {
        set index [$textWidget index $index]
        if {$dir == -1} {
            set pair [lreverse $pair]
            set defaultBoundary DOC-HEAD
            set condition {$i > 0}
        } else {
            set defaultBoundary DOC-END
            set textEndLine [expr int([$textWidget index end])]
            set condition {$i < $textEndLine}
        }

        set i [expr int($index)]
        set e [lindex $pair 1]
        if {$dir == -1} {
            set from $i.0 ; set to $index
        } else {
            set from $index ; set to "$index lineend"
        }
        set stack {}
        set boundary [do {
            set line [$textWidget get $from $to]
            set indices [my getPairIndices $line $pair]
            if {$dir == -1} {set indices [lreverse $indices]}
            # puts "$line -> $indices"
            foreach id $indices {
                if [catch {my pushPairStack $stack [string index $line $id] $pair} stack] {
                    return -code error disorder
                    # return
                }
                # puts "$id: [string index $line $id] -> $stack >> [list $e]"
                if {$stack == [list $e]} {
                    # puts $id
                    return [$textWidget index $from+${id}c]
                }
            }
            incr i $dir
            set from $i.0; set to "$i.0 lineend"
        } while $condition]
        if {$boundary != {}} {
            return $boundary
        }
        return -code error $defaultBoundary
    }

    method getPairHead {textWidget index pair} {
        return [my getPairBoundary $textWidget $index $pair -1]
    }

    method getPairEnd {textWidget index pair} {
        return [my getPairBoundary $textWidget $index $pair 1]
    }

    method getWordHeadIndex {textWidget index} {
        # the index is in or adhere to a word
        set pre [$textWidget get "$index linestart" $index]
        if {[set id [lindex [lindex [regexp -inline -indices {[\w:]+$} $pre] 0] 0]] == {}} {
            return $index
        }
        return [$textWidget index "$index linestart +${id}c"]
    }

    method getWordEndIndex {textWidget index} {
        # the index is in or adhere to a word
        set post [$textWidget get $index "$index lineend"]
        if {[set id [lindex [lindex [regexp -inline -indices {^[\w:]+} $post] 0] 1]] == {}} {
            return $index
        }
        return [$textWidget index "$index +${id}c +1c"]
    }
}
